package com.unswift.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

import com.github.difflib.text.DiffRow;
import com.github.difflib.text.DiffRowGenerator;
import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.exception.CoreException;

@Api(value="文件公共操作类", author="unswift", date="2022-12-16", version="1.0.0")
public final class FileUtils {
	
	@ApiField("文件换行符")
	private final static String nextLine = System.getProperty("line.separator");
	
	@ApiMethod(value="获取当前系统的文件分割符，window为\\，linux为/")
	public static String separator(){
		return File.separator;
	}
	
	@ApiMethod(value="获取文件换行符", returns=@ApiField("换行符"))
	public static String nextLine() {
		return nextLine;
	}
	
	@ApiMethod(value="如果文件路径表示的是一个真实存在的文件，则删除文件", params = {@ApiField("文件路径")}, returns = @ApiField("是否删除，true：已删除，false：无法找到附件"))
	public static boolean delete(String file) {
		return delete(new File(file));
	}
	
	@ApiMethod(value="如果文件存在，则删除文件", params = {@ApiField("文件")}, returns = @ApiField("是否删除，true：已删除，false：无法找到附件"))
	public static boolean delete(File file) {
		if(file.exists()) {
			return file.delete();
		}
		return false;
	}

	@ApiMethod(value="根据文件路径创建文件", params={@ApiField("文件路径"), @ApiField("如果存在是否删除，true：删除，false：不删除")})
	public static void createFile(String file, boolean existsDelete) {
		createFile(new File(file), existsDelete);
	}

	@ApiMethod(value="根据文件对象创建文件", params={@ApiField("文件对象"), @ApiField("如果存在是否删除，true：删除，false：不删除")})
	public static void createFile(File file, boolean existsDelete) {
		try {
			if (!file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}
			if (file.exists()) {
				if (existsDelete) {
					file.delete();
					file.createNewFile();
				}
			} else {
				file.createNewFile();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("create.file.exception", e, file.getAbsolutePath(), e.getMessage());
		}
	}
	
	@ApiMethod(value="根据文件目录路径创建目录", params={@ApiField("文件目录路径"), @ApiField("如果存在是否删除，true：删除，false：不删除")})
	public static void createFolder(String file, boolean existsDelete) {
		createFolder(new File(file), existsDelete);
	}
	
	@ApiMethod(value="根据文件目录对象创建目录", params={@ApiField("文件目录对象"), @ApiField("如果存在是否删除，true：删除，false：不删除")})
	public static void createFolder(File file, boolean existsDelete) {
		if(existsDelete && file.exists()){
			file.delete();
		}else if(!file.exists()){
			file.mkdirs();
		}
	}

	@ApiMethod(value="根据文件路径读取文件内容", params={@ApiField("文件路径"), @ApiField("文件编码")}, returns=@ApiField("文件内容"))
	public static String reader(String file, String charset) {
		return reader(new File(file), charset);
	}

	@ApiMethod(value="根据文件对象读取文件内容", params={@ApiField("文件对象"), @ApiField("文件编码")}, returns=@ApiField("文件内容"))
	public static String reader(File file, String charset) {
		try {
			return reader(new FileInputStream(file), charset);
		} catch (CoreException e){
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, file.getAbsolutePath(), e.getMessage());
		}
	}
	
	@ApiMethod(value="根据文件输入流对象读取文件内容", params={@ApiField("文件对象"), @ApiField("文件编码")}, returns=@ApiField("文件内容"))
	public static String reader(InputStream stream, String charset) {
		try {
			return reader(new InputStreamReader(stream, charset), true);
		} catch (CoreException e){
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, "", e.getMessage());
		}
	}
	
	@ApiMethod(value="根据文件输入流读取文件内容", params={@ApiField("文件输入流"), @ApiField("读取完成是否关闭流")})
	public static String reader(InputStreamReader reader, boolean close) {
		try {
			StringBuilder content = new StringBuilder();
			char[] buff = new char[1024];
			int length;
			while ((length = reader.read(buff)) > 0) {
				content.append(buff, 0, length);
			}
			return content.toString();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, "", e.getMessage());
		} finally {
			if(close){
				close(reader);
			}
		}
	}
	
	@ApiMethod(value="读取java socket文件流", params={@ApiField("输入流"), @ApiField("输入流结束字符，并不一定要等文件结束"), @ApiField("读取完成是否关闭文件流")}, returns=@ApiField("文件流内容"))
	public static String socketReader(InputStreamReader reader, char closeEnd, boolean close) {
		try {
			StringBuilder content = new StringBuilder();
			char[] buff = new char[1024];
			int length;
			while ((length = reader.read(buff)) > 0) {
				content.append(buff, 0, length);
				if(content.charAt(content.length()-1)==closeEnd){
					break;
				}
			}
			return content.toString();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, "", e.getMessage());
		} finally {
			if(close){
				close(reader);
			}
		}
	}
	
	@ApiMethod(value="文件行读取模式，一次读取一行，采用类FileLine处理行数据", params={@ApiField("文件路径"), @ApiField("文件编码"), @ApiField("行处理对象")}, returns=@ApiField("读取结果，true：读取成功，false：读取失败"))
	public static boolean readerLine(String file, String charset, FileLine lineBack) {
		return readerLine(new File(file), charset, lineBack);
	}
	
	@ApiMethod(value="文件行读取模式，一次读取一行，采用类FileLine处理行数据", params={@ApiField("文件对象"), @ApiField("文件编码"), @ApiField("行处理对象")}, returns=@ApiField("读取结果，true：读取成功，false：读取失败"))
	public static boolean readerLine(File file, String charset, FileLine lineBack) {
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
			String line;
			boolean pass=true;
			int row=0;
			while ((line = reader.readLine()) != null) {
				pass=lineBack.handleLine(line, row);
				if(!pass){
					break;
				}
				row++;
			}
			lineBack.endFile(row);
			return pass;
		} catch(CoreException e){
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, file.getAbsolutePath(), e.getMessage());
		} finally {
			close(reader);
			lineBack.closed();
		}
	}
	
	@ApiMethod(value="文件行读取模式，返回行列表，对于比较小的文件可使用此方法", params={@ApiField("文件路径"), @ApiField("文件编码")}, returns=@ApiField("文件行列表"))
	public static List<String> readerLine(String file, String charset){
		return readerLine(new File(file), charset);
	}
	
	@ApiMethod(value="文件行读取模式，返回行列表，对于比较小的文件可使用此方法", params={@ApiField("文件对象"), @ApiField("文件编码")}, returns=@ApiField("文件行列表"))
	public static List<String> readerLine(File file, String charset){
		BufferedReader reader = null;
		List<String> lineContentList=new ArrayList<String>();
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
			String line;
			while ((line = reader.readLine()) != null) {
				lineContentList.add(line);
			}
			return lineContentList;
		} catch(CoreException e){
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, file.getAbsolutePath(), e.getMessage());
		} finally {
			close(reader);
		}
	}
	
	@ApiMethod(value="将文件内容转为行列表", params={@ApiField("文件内容")}, returns=@ApiField("文件行列表"))
	public static List<String> contentToLine(String content){
		return ObjectUtils.asList(content.split(nextLine()));
	}
	
	@ApiMethod(value="将文件行列表转为内容", params={@ApiField("行列表")}, returns=@ApiField("内容"))
	public static String lineToContent(List<String> lineList){
		StringBuilder content=new StringBuilder();
		for (String line : lineList) {
			content.append(line).append(nextLine());
		}
		return content.toString();
	}
	
	@ApiMethod(value="文件比较差异", params={@ApiField("旧文件路径"), @ApiField("新的文件路径"), @ApiField("文件编码")}, returns=@ApiField("返回差异"))
	public static List<DiffRow> compareLines(String oldFile, String newFile, String charset){
		return compareLines(new File(oldFile), new File(newFile), charset);
	}
	
	@ApiMethod(value="文件比较差异", params={@ApiField("旧文件"), @ApiField("新的文件"), @ApiField("文件编码")}, returns=@ApiField("返回差异"))
	public static List<DiffRow> compareLines(File oldFile, File newFile, String charset){
		return compareLines(readerLine(oldFile, charset), readerLine(newFile, charset));
	}
	
	@ApiMethod(value="文件内容比较差异", params={@ApiField("旧文件内容"), @ApiField("新的文件内容")}, returns=@ApiField("返回差异"))
	public static List<DiffRow> compareLines(String oldContent, String newContent){
		return compareLines(contentToLine(oldContent), contentToLine(newContent));
	}
	
	@ApiMethod(value="文件行比较差异", params={@ApiField("就得文件行列表"), @ApiField("新的文件行列表")}, returns=@ApiField("返回差异"))
	public static List<DiffRow> compareLines(List<String> oldLineList, List<String> newLineList){
		DiffRowGenerator generator = DiffRowGenerator.create().showInlineDiffs(true).mergeOriginalRevised(true).inlineDiffByWord(true).build();
		return generator.generateDiffRows(oldLineList, newLineList);
	}
	
	@ApiMethod(value="文件倒叙行读取模式，一次读取一行，采用类FileLine处理行数据", params={@ApiField("文件路径"), @ApiField("文件编码"), @ApiField("行处理对象"), @ApiField("开始的文件索引"), @ApiField("结束的文件索引")}, returns=@ApiField("读取结果，true：读取成功，false：读取失败"))
	public static boolean readerLineFlashback(String file, String charset, FileLine lineBack, int limitStart, int limitSize) {
		return readerLineFlashback(new File(file), charset, lineBack, limitStart, limitSize);
	}
	
	@ApiMethod(value="文件倒叙行读取模式，一次读取一行，采用类FileLine处理行数据", params={@ApiField("文件对象"), @ApiField("文件编码"), @ApiField("行处理对象"), @ApiField("开始的文件索引"), @ApiField("结束的文件索引")}, returns=@ApiField("读取结果，true：读取成功，false：读取失败"))
	public static boolean readerLineFlashback(File file, String charset, FileLine lineBack, int limitStart, int limitSize) {
		RandomAccessFile inputStream=null;
		try {
			inputStream=new RandomAccessFile(file, "r");
			long length=inputStream.length();
			long start=inputStream.getFilePointer();
			long nextEnd=start+length-1;
			inputStream.seek(nextEnd);
			int c=-1;
			boolean pass=true;
			int rowIndex=0, limitEnd=limitSize==-1?-1:limitStart+limitSize;
			String line;
			while(nextEnd> start){
				c=inputStream.read();
				if(c<255){
					if(nextLine().endsWith(String.valueOf((char)c))){
						if(rowIndex>=limitStart && (limitEnd==-1 || rowIndex<limitEnd)){
							line=inputStream.readLine();
							if(ObjectUtils.isEmpty(line)){
								pass=lineBack.handleLine(line, rowIndex);
							}else{
								pass=lineBack.handleLine(new String(line.getBytes("ISO-8859-1"), charset), rowIndex);
							}
							if(!pass){
								break;
							}
						}
						rowIndex++;
					}
				}
				if(limitEnd!=-1 && rowIndex>=limitEnd){
					break;
				}
				nextEnd--;
				inputStream.seek(nextEnd);
			}
			if(rowIndex>0 && nextEnd==0){
				line=inputStream.readLine();
				if(ObjectUtils.isEmpty(line)){
					pass=lineBack.handleLine(line, rowIndex);
				}else{
					pass=lineBack.handleLine(new String(line.getBytes("ISO-8859-1"), charset), rowIndex);
				}
				rowIndex++;
			}
			lineBack.endFile(rowIndex);
			return pass;
		} catch(CoreException e){
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("reader.file.exception", e, file.getAbsolutePath(), e.getMessage());
		} finally {
			close(inputStream);
			lineBack.closed();
		}
	}
	
	@ApiMethod(value="替换文件内容，并输入到指定目标文件", params={@ApiField("源文件路径"), @ApiField("目标文件路径"), @ApiField("文件编码"), @ApiField("需要替换的内容"), @ApiField("替换后的内容")})
	public static void replace(String source, String target, String charset, String sourceContent, String targetContent){
		replace(new File(source), new File(target), charset, sourceContent, targetContent);
	}
	
	@ApiMethod(value="替换文件内容，并输入到指定目标文件", params={@ApiField("源文件对象"), @ApiField("目标文件对象"), @ApiField("文件编码"), @ApiField("需要替换的内容"), @ApiField("替换后的内容")})
	public static void replace(File source, File target, String charset, String sourceContent, String targetContent){
		createFile(target, true);
		BufferedReader reader = null;
		BufferedWriter writer = null;
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream(source), charset));
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(target), charset));
			String line;
			while ((line = reader.readLine()) != null) {
				writer.write(line.replace(sourceContent, targetContent));
				writer.write(nextLine());
			}
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("replace.file.exception", e, source.getAbsolutePath(), sourceContent, targetContent, e.getMessage());
		} finally {
			close(reader);
			close(writer);
		}
	}
	
	@ApiMethod(value="拷贝文件-指定编码", params={@ApiField("源文件对象"), @ApiField("目标文件对象"), @ApiField("文件编码"), @ApiField("是否删除源文件，true：删除,false：不删除")})
	public static void copy(File source, File target, String charset, boolean deleteSource){
		createFile(target, true);
		BufferedReader reader = null;
		BufferedWriter writer = null;
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream(source), charset));
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(target), charset));
			String line;
			while ((line = reader.readLine()) != null) {
				writer.write(line);
				writer.write(nextLine());
			}
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("copy.file.exception", e, source.getAbsolutePath(), e.getMessage());
		} finally {
			close(reader);
			close(writer);
		}
		if(deleteSource){
			source.delete();
		}
	}
	
	@ApiMethod(value="拷贝文件-不指定编码", params={@ApiField("源文件对象"), @ApiField("目标文件对象"), @ApiField("是否删除源文件，true：删除,false：不删除")})
	public static void copy(File source, File target, boolean deleteSource){
		createFile(target, true);
		InputStream input = null;
		OutputStream output = null;
		try {
			FileUtils.createFile(target, true);
			input = new FileInputStream(source);
			output = new FileOutputStream(target);
			byte[] buffer = new byte[1024];
	        int length;
			while ((length = input.read(buffer))>0) {
				output.write(buffer, 0, length);
			}
			output.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("copy.file.exception", e, source.getAbsolutePath(), e.getMessage());
		} finally {
			close(input);
			close(output);
		}
		if(deleteSource){
			source.delete();
		}
	}
	
	@ApiMethod(value="拷贝文件-不指定编码", params={@ApiField("源文件对象"), @ApiField("目标文件对象"), @ApiField("是否删除源文件，true：删除,false：不删除")})
	public static void copy(File source, OutputStream output, boolean deleteSource){
		InputStream input = null;
		try {
			input = new FileInputStream(source);
			byte[] buffer = new byte[1024];
	        int length;
			while ((length = input.read(buffer))>0) {
				output.write(buffer, 0, length);
			}
			output.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("copy.file.exception", e, source.getAbsolutePath(), e.getMessage());
		} finally {
			close(input);
		}
		if(deleteSource){
			source.delete();
		}
	}
	
	@ApiMethod(value="将文件内容写入文件", params={@ApiField("文件路径"), @ApiField("写入内容"), @ApiField("文件编码"), @ApiField("是否追加到文件末尾，true：追加，false：不追加")})
	public static void writer(String file, String content, String charset, boolean append) {
		writer(new File(file), content, charset, append);
	}
	
	@ApiMethod(value="将文件内容写入文件", params={@ApiField("文件对象"), @ApiField("写入内容"), @ApiField("文件编码"), @ApiField("是否追加到文件末尾，true：追加，false：不追加")})
	public static void writer(File file, String content, String charset, boolean append) {
		BufferedWriter writer = null;
		try {
			createFile(file, false);
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset));
			writer.write(content);
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("writer.file.exception", e, file.getAbsolutePath(), e.getMessage());
		} finally {
			close(writer);
		}
	}
	
	@ApiMethod(value="将一行数据写入文件，在写入前优先写入换行", params={@ApiField("文件路径"), @ApiField("写入行内容"), @ApiField("文件编码")}, returns=@ApiField("文件输出流对象，写入行数据可能未完成，可直接使用writerLine的流写入方法，无需再打开文件输出流"))
	public static BufferedWriter writerLine(String file, String line, String charset) {
		return writerLine(new File(file), line, charset);
	}
	@ApiMethod(value="将一行数据写入文件，在写入前优先写入换行", params={@ApiField("文件对象"), @ApiField("写入行内容"), @ApiField("文件编码")}, returns=@ApiField("文件输出流对象，写入行数据可能未完成，可直接使用writerLine的流写入方法，无需再打开文件输出流"))
	public static BufferedWriter writerLine(File file, String line, String charset) {
		BufferedWriter writer = null;
		try {
			boolean newFile=file.length()==0;
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), charset));
			if(!newFile){
				writer.write(nextLine());
			}
			writer.write(line);
			writer.write(nextLine());
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("writer.file.exception", e, file.getAbsolutePath(), e.getMessage());
		} finally {
			close(writer);
		}
		return writer;
	}
	
	@ApiMethod(value="将一行数据写入文件，在写入前优先写入换行", params={@ApiField("文件输出流对象"), @ApiField("写入行内容"), @ApiField("是否写入完成，true：写入完成，false：未写入完成")})
	public static void writerLine(BufferedWriter writer, String content, boolean complate) {
		try {
			writer.write(nextLine());
			writer.write(content);
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("writer.file.exception", e, "", e.getMessage());
		} finally {
			if(complate){
				close(writer);
			}
		}
	}
	
	@ApiMethod(value="再文件指定位置处插入内容，并覆盖后续内容", params={@ApiField("文件路径"), @ApiField("插入位置"), @ApiField("插入内容"), @ApiField("文件编码")})
	public static void insertCover(String file, int insertIndex, String content, String charset){
		insertCover(new File(file), insertIndex, content, charset);
	}
	
	@ApiMethod(value="再文件指定位置处插入内容，并覆盖后续内容", params={@ApiField("文件对象"), @ApiField("插入位置"), @ApiField("插入内容"), @ApiField("文件编码")})
	public static void insertCover(File file, long insertIndex, String content, String charset){
		RandomAccessFile insertStream=null;
		try {
			createFile(file, false);
			insertStream=new RandomAccessFile(file, "rwd");
			insertStream.seek(insertIndex);
			insertStream.write(content.getBytes("ISO-8859-1"));
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("writer.file.exception", e, file.getAbsolutePath(), e.getMessage());
		} finally {
			close(insertStream);
		}
	}
	
	@ApiMethod(value="将文件写入流缓存数据写入文件", params=@ApiField("文件写入流"))
	public static void flush(BufferedWriter writer) {
		try {
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("writer.flush.file.exception", e, "", e.getMessage());
		}
	}

	public static void close(Closeable close) {
		try {
			if (close != null) {
				close.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("close.file.exception", e, "", e.getMessage());
		}
	}
	
	@Api(value="文件行数据处理类", author="unswift", date="2022-12-16", version="1.0.0")
	public static abstract class FileLine {

		@ApiMethod(value="行数据处理方法，使用者通过实现此方法实现行数据业务处理", params={@ApiField("行数据"), @ApiField("第几行，从零开始")})
		public boolean handleLine(String line, int row){return true;};

		@ApiMethod(value="文件流处理结束时触发", params=@ApiField("总行数"))
		public void endFile(int row){};
		
		@ApiMethod(value="文件流关闭时触发")
		public void closed(){};
		
	}
}
